﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MBWebService.Core.Interfaces;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Diagnostics;
using MBWebService.Core;
using MBWebService.Core.Classes;
using MBWebService.Core.Logging;

namespace MBWebService.Service
{
    [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
    public class MediaBrowserServer : IMediaBrowserServer
    {
        ServiceHost host;
        Dictionary<string, DeviceChannelPair> channels = new Dictionary<string, DeviceChannelPair>();
        List<PlaybackDevice> devices;
        
        #region IMediaBrowserServer Members

        public void Subscribe(PlaybackDevice player)
        {
            var callbackChannel = OperationContext.Current.GetCallbackChannel<IMediaBrowserCallback>();
            if (channels.ContainsKey(player.Name))
                channels.Remove(player.Name);

            if (callbackChannel != null)
            {
                channels.Add(player.Name, new DeviceChannelPair { Channel = callbackChannel, Device = player });
                RefreshDeviceList();
            }
        }

        public void Unsubscribe(string deviceName)
        {
            if (channels.ContainsKey(deviceName))
            {
                channels.Remove(deviceName);
                RefreshDeviceList();
            }
        }

        public bool Test() { return true; }

        #endregion

        public void Initialise()
        {
            devices = new List<PlaybackDevice>();

            try
            {
                this.host = new ServiceHost(this, AppSettings.LocalPublisherUri);
                WSDualHttpBinding binding = new WSDualHttpBinding(WSDualHttpSecurityMode.None);
                binding.ClientBaseAddress = new Uri(string.Format(@"http://localhost:{0}/MbClient", AppSettings.UserSettings.BasePort - 2));

                this.host.AddServiceEndpoint(typeof(IMediaBrowserServer), binding, string.Empty);
                this.host.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true });

                this.host.Open();
            }
            catch (Exception ex)
            {
                Logger.Instance.LogMessage("Error Initialising publishing service; ERROR: {0}", ex.Message);
                Logger.Instance.LogMessage("This means that you'll be able to browse data, but clients cannot connect to the service.");
                throw;
            }
        }

        internal T PerformAction<T>(string player, Func<IMediaBrowserCallback, T> func)
        {
            if (player == null)
            {
                var device = ResolveDeviceName(player);
                player = device.Name;
            }

            if (channels.ContainsKey(player))
            {
                try
                {
                    IMediaBrowserCallback callback = channels[player].Channel;
                    return func.Invoke(callback);
                }
                catch (Exception ex)
                {
                    string message = string.Format("Error performing action on '{0}': {1}", player, ex.Message);
                    Logger.Instance.LogMessage(message);
                    throw new FaultException<InvalidOperationException>(new InvalidOperationException(message));
                }
            }
            else
            {
                string message = string.Format("Player '{0}' not found.", player);
                Logger.Instance.LogMessage(message);
                throw new FaultException<InvalidOperationException>(new InvalidOperationException(message));
            }
        }

        private void RefreshDeviceList()
        {
            List<PlaybackDevice> tempDevices = new List<PlaybackDevice>();
            foreach (var deviceChannel in channels.Values)
            {
                tempDevices.Add(deviceChannel.Device);
            }

            devices = tempDevices;
        }

        public PlaybackDevice ResolveDeviceName(string deviceName)
        {
            PlaybackDevice device = null;

            if (string.IsNullOrEmpty(deviceName))
            {
                // Use default unless otherwise stated.
                string sessionName = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
                int index = sessionName.LastIndexOf(@"\");
                if (index > 0)
                {
                    sessionName = sessionName.Substring(index + 1);
                }

                deviceName = sessionName;
            }

            bool success = false;

            foreach (var thing in channels)
            {
                if (!success)
                {
                    var deviceCur = thing.Value.Device;
                    if (deviceCur.Name == deviceName ||
                        deviceCur.UserName == deviceName)
                    {
                        success = true;
                        device = deviceCur;
                    }
                }
            }

            if (!success)
            {
                // create default local
                device = new PlaybackDevice
                {
                    Name = deviceName,
                    UserName = deviceName,
                    IsMediaCenter = true
                };
            }

            return device;
        }

        internal void PerformAction(string player, Action<IMediaBrowserCallback> action)
        {
            if (player == null)
            {
                // Use default unless otherwise stated.
                string sessionName = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
                int index = sessionName.LastIndexOf(@"\");
                if (index > 0)
                {
                    sessionName = sessionName.Substring(index + 1);
                }

                player = sessionName;
            }
            IMediaBrowserCallback callback = channels[player].Channel;
            action.Invoke(callback);
        }

        public PlaybackDevice[] AttachedDevices
        {
            get
            {
                return devices.ToArray();
            }
        }

        private class DeviceChannelPair
        {
            public IMediaBrowserCallback Channel { get; set; }
            public PlaybackDevice Device { get; set; }
        }
    }
}
